using System;
using System.Data;
using System.Data.SqlClient;
using System.Drawing;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.ComponentModel;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Timers;
using System.Threading;
using System.Text.RegularExpressions;
using System.Windows.Forms;

namespace FileHound {
	public class BaseHound : System.Windows.Forms.Form {

		#region ======================== Controls =====================================
		protected System.Windows.Forms.StatusBar stBar;
		protected System.Windows.Forms.StatusBarPanel stBarStatus;
		protected System.Windows.Forms.StatusBarPanel stBarErrorNumber;
		protected System.Windows.Forms.StatusBarPanel stBarFiles;
		protected System.Windows.Forms.StatusBarPanel stBarTotal;
		private System.ComponentModel.IContainer components;
		protected System.Windows.Forms.TextBox txtError;
		protected System.Windows.Forms.ListBox lstInfo;
		protected System.Windows.Forms.ListBox lstQue;
		#endregion ====================================================================

		#region ======================== Config =======================================
		protected int NumberOfRetries { get { return int.Parse(Common.GetPropertyFromConfig("NumberOfRetries")); } }
		protected string dbConn { get { return Common.GetPropertyFromConfig("conn"); } }
		protected string dbImages { get { return Common.GetPropertyFromConfig("connImages"); } }
		protected string ContractAdLibAccuracy { get { return Common.GetPropertyFromConfig("ContractAdLibAccuracy"); } }
		protected string ApplicationAdLibAccuracy { get { return Common.GetPropertyFromConfig("ApplicationAdLibAccuracy"); } }
		private int ThreadTimeOut { get { return int.Parse(Common.GetPropertyFromConfig("ThreadTimeOut")); } }
		protected class WebService {
			public static int TimeOut { get { return Common.WebService.TimeOut; } }
		}
		#endregion ====================================================================

		#region ======================== Properties ===================================
		internal FileHound.Properties.Settings Settings;
		protected int TotalErrors;
		protected int OkFiles;
		protected int TotalFiles;

		#region ErrorText
		protected delegate void ErrorTextHandler(string errorText);
		protected string ErrorText { set { txtError.Invoke(new ErrorTextHandler(ErrorText_UI), value); } }
		private void ErrorText_UI(string errorText) {
			txtError.AppendText(" ******* " + DateTime.Now + ": " + errorText.Replace("\n", Environment.NewLine) + Environment.NewLine);
			if (!txtError.Focused) txtError.Focus();
			txtError.ScrollToCaret();
			Application.DoEvents();
		}
		#endregion

		#region Status
		protected delegate void StatusHandler(string status);
		protected string Status { set { stBar.Invoke(new StatusHandler(Status_UI), value); } }
		private void Status_UI(string status) {
			stBarStatus.Text = status + " (" + DateTime.Now.ToString("hh:mm:ss") + ")"; Application.DoEvents();
		}

		#endregion

		#region BarTotal
		protected delegate void BarTotalHandler(int total);
		protected int BarTotal { set { stBar.Invoke(new BarTotalHandler(BarTotal_UI), value); } }
		private void BarTotal_UI(int total) {
			stBarTotal.Text = "Received: " + total;
		}
		#endregion

		#region BarErrorNumber
		protected delegate void BarErrorNumberHandler(int total);
		protected int BarErrorNumber { set { stBar.Invoke(new BarErrorNumberHandler(BarErrorNumber_UI), value); } }
		private void BarErrorNumber_UI(int totalErrors) {
			stBarErrorNumber.Text = "Errors: " + totalErrors;
		}
		#endregion

		protected Queue queFiles = null;
		protected bool CloseMe = false;
		protected Thread processThread;
		private System.Timers.Timer threadTimer = new System.Timers.Timer(15000.0);
		private bool IsThreadAlive { get { return processThread != null && processThread.IsAlive; } }
		private bool IsThreadAborting { get { return processThread != null && processThread.ThreadState == ThreadState.AbortRequested; } }
		public int ThreadsCountMax { get { return Settings.ThreadsCountMax == 0 ? Environment.ProcessorCount : Settings.ThreadsCountMax; } }
		protected ArrayList ThreadList = new ArrayList();
		#endregion ====================================================================

		#region ======================== Windows Form Designer ========================
		private void InitializeComponent() {
			System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(BaseHound));
			this.stBar = new System.Windows.Forms.StatusBar();
			this.stBarStatus = new System.Windows.Forms.StatusBarPanel();
			this.stBarFiles = new System.Windows.Forms.StatusBarPanel();
			this.stBarErrorNumber = new System.Windows.Forms.StatusBarPanel();
			this.stBarTotal = new System.Windows.Forms.StatusBarPanel();
			this.txtError = new System.Windows.Forms.TextBox();
			this.lstQue = new System.Windows.Forms.ListBox();
			this.lstInfo = new System.Windows.Forms.ListBox();
			((System.ComponentModel.ISupportInitialize)(this.stBarStatus)).BeginInit();
			((System.ComponentModel.ISupportInitialize)(this.stBarFiles)).BeginInit();
			((System.ComponentModel.ISupportInitialize)(this.stBarErrorNumber)).BeginInit();
			((System.ComponentModel.ISupportInitialize)(this.stBarTotal)).BeginInit();
			this.SuspendLayout();
			// 
			// stBar
			// 
			this.stBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left)
									| System.Windows.Forms.AnchorStyles.Right)));
			this.stBar.Dock = System.Windows.Forms.DockStyle.None;
			this.stBar.Location = new System.Drawing.Point(0, 290);
			this.stBar.Name = "stBar";
			this.stBar.Panels.AddRange(new System.Windows.Forms.StatusBarPanel[] {
            this.stBarStatus,
            this.stBarFiles,
            this.stBarErrorNumber,
            this.stBarTotal});
			this.stBar.ShowPanels = true;
			this.stBar.Size = new System.Drawing.Size(460, 23);
			this.stBar.TabIndex = 3;
			// 
			// stBarStatus
			// 
			this.stBarStatus.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Spring;
			this.stBarStatus.MinWidth = 0;
			this.stBarStatus.Name = "stBarStatus";
			this.stBarStatus.Width = 322;
			// 
			// stBarFiles
			// 
			this.stBarFiles.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Contents;
			this.stBarFiles.MinWidth = 0;
			this.stBarFiles.Name = "stBarFiles";
			this.stBarFiles.Text = "Files";
			this.stBarFiles.Width = 38;
			// 
			// stBarErrorNumber
			// 
			this.stBarErrorNumber.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Contents;
			this.stBarErrorNumber.MinWidth = 0;
			this.stBarErrorNumber.Name = "stBarErrorNumber";
			this.stBarErrorNumber.Text = "Errors";
			this.stBarErrorNumber.Width = 45;
			// 
			// stBarTotal
			// 
			this.stBarTotal.AutoSize = System.Windows.Forms.StatusBarPanelAutoSize.Contents;
			this.stBarTotal.MinWidth = 0;
			this.stBarTotal.Name = "stBarTotal";
			this.stBarTotal.Text = "Total";
			this.stBarTotal.Width = 39;
			// 
			// txtError
			// 
			this.txtError.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
									| System.Windows.Forms.AnchorStyles.Left)
									| System.Windows.Forms.AnchorStyles.Right)));
			this.txtError.Location = new System.Drawing.Point(175, 112);
			this.txtError.Multiline = true;
			this.txtError.Name = "txtError";
			this.txtError.ScrollBars = System.Windows.Forms.ScrollBars.Vertical;
			this.txtError.Size = new System.Drawing.Size(282, 174);
			this.txtError.TabIndex = 2;
			// 
			// lstQue
			// 
			this.lstQue.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
									| System.Windows.Forms.AnchorStyles.Left)));
			this.lstQue.Location = new System.Drawing.Point(0, 112);
			this.lstQue.Name = "lstQue";
			this.lstQue.Size = new System.Drawing.Size(169, 173);
			this.lstQue.TabIndex = 1;
			// 
			// lstInfo
			// 
			this.lstInfo.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left)
									| System.Windows.Forms.AnchorStyles.Right)));
			this.lstInfo.Location = new System.Drawing.Point(0, 0);
			this.lstInfo.Name = "lstInfo";
			this.lstInfo.ScrollAlwaysVisible = true;
			this.lstInfo.Size = new System.Drawing.Size(460, 108);
			this.lstInfo.TabIndex = 0;
			// 
			// BaseHound
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(460, 313);
			this.Controls.Add(this.lstInfo);
			this.Controls.Add(this.lstQue);
			this.Controls.Add(this.txtError);
			this.Controls.Add(this.stBar);
			this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
			this.Name = "BaseHound";
			this.ShowInTaskbar = false;
			this.Text = "File Hound";
			this.Closing += new System.ComponentModel.CancelEventHandler(this.Hound_Closing);
			this.Load += new System.EventHandler(this.Hound_Load);
			((System.ComponentModel.ISupportInitialize)(this.stBarStatus)).EndInit();
			((System.ComponentModel.ISupportInitialize)(this.stBarFiles)).EndInit();
			((System.ComponentModel.ISupportInitialize)(this.stBarErrorNumber)).EndInit();
			((System.ComponentModel.ISupportInitialize)(this.stBarTotal)).EndInit();
			this.ResumeLayout(false);
			this.PerformLayout();

		}
		protected override void Dispose(bool disposing) {
			if (disposing) {
				if (components != null) {
					components.Dispose();
				}
			}
			base.Dispose(disposing);
		}
		#endregion ====================================================================

		#region ======================== Form =========================================
		public BaseHound() {
			InitializeComponent();
			CheckForIllegalCrossThreadCalls = false;
			Settings = new FileHound.Properties.Settings();
		}
		protected void Hound_Load(object sender, System.EventArgs e) {
			InitTimer();
			queFiles = Queue.Synchronized(new Queue());
			SetDefaultStatus();
		}
		protected void Hound_Closing(object sender, System.ComponentModel.CancelEventArgs e) {
			WaitForThread();
		}
		private void WaitForThread() {
			CloseMe = true;
			ErrorText = "Closing application. It may take a while. Wait...";
			while (ActiveThreadsCount() > 0) {
				try {
					foreach (Thread t in ThreadList)
						if (!t.IsAlive) ThreadList.Remove(t);
				} catch (InvalidOperationException) { }
				Application.DoEvents();
			}
		}
		protected int ActiveThreadsCount() {
			for (int i = 0; i < ThreadList.Count; )
				if (!((Thread)ThreadList[i]).IsAlive) ThreadList.Remove(ThreadList[i]);
				else i++;
			return ThreadList.Count;
		}
		protected bool ThreadCanRun(Thread t) {
			for (int i = 0; i < ThreadsCountMax && i < ActiveThreadsCount(); i++)
				if (t == ThreadList[i]) return true;
			return false;
		}
		#endregion ====================================================================

		#region ======================== File Processing ==============================
		//[MethodImplAttribute(MethodImplOptions.Synchronized)]
		private delegate void QueListHandler(ProcessingFile pf);
		protected void PopFromQue(ProcessingFile pf) {
			lstQue.Invoke(new QueListHandler(PopFromQue_UI), pf);
		}
		protected void PopFromQue_UI(ProcessingFile pf) {
			if (lstQue.Items.Count == 0) return;
			foreach (object fileName in lstQue.Items)
				if ((fileName + "") == pf.Name) {
					lstQue.Items.Remove(fileName);
					break;
				}
			//lstQue.Items.RemoveAt(0);
			//queFiles.Dequeue();
		}
		//[MethodImplAttribute(MethodImplOptions.Synchronized)]
		protected void AddToQue(ProcessingFile file) {
			lstQue.Invoke(new QueListHandler(AddToQue_UI), file);
		}
		protected void AddToQue_UI(ProcessingFile file) {
			if (!file.CheckFilter()) return;
			lock (queFiles.SyncRoot) {
				foreach (ProcessingFile pf in queFiles.ToArray())
					if (pf.FullName == file.FullName) return;
				queFiles.Enqueue(file);
				lstQue.Items.Add(file.Name);
			}
			//if( !IsThreadAlive )
			StartProcessingThread();
			//else ErrorText = "Thread is busy"+(IsThreadAborting?" (Aborting)":"")+"!";
		}
		private void StartProcessingThread() {
			if (ActiveThreadsCount() == ThreadsCountMax) return;
			Thread t = new Thread(new ThreadStart(StartProcessing));
			t.Priority = ThreadPriority.BelowNormal;
			t.Start();
			ThreadList.Add(t);
		}
		//[MethodImplAttribute(MethodImplOptions.Synchronized)]
		protected void StartProcessing() {
			while (queFiles.Count > 0) {
				if (IsThreadAborting) {
					ErrorText = "StartProcessing: Thread is aborting ...";
					return;
				}
				//threadTimer.Start();
				string status = "UnKnown";
				DateTime dtStart = DateTime.Now;
				if (CloseMe) break;
				ProcessingFile pf = null;
				try {
					lock (queFiles.SyncRoot) {
						pf = (ProcessingFile)queFiles.Dequeue();
					}
					if (!File.Exists(pf.FullName)) {
						pf.Done = true;
						continue;
					}
					status = ProcessFile(pf) ? "Processed" : "Skipped";
					pf.Done = true;
				} catch (Exception ex) {
					status = "ERROR";
					if (pf == null) ErrorText = "Queue is empty.";
					else {
						pf.CheckRetryCount(ex);
						ProcessingErrorHandler(ex, pf);
					}
				} finally {
					//threadTimer.Stop();
					if ( pf != null && pf.Done) {
						PopFromQue(pf);
						ProcessingCompleteHandler(pf, dtStart, status);
					}
				}
			}
		}
		private bool HasAbortThreadException(Exception exc) {
			for (; exc != null; exc = exc.InnerException)
				if (exc.GetType() == typeof(ThreadAbortException)) return true;
			return false;
		}
		public virtual bool ProcessFile(ProcessingFile file) {
			throw new Exception("Hound.ProcessFile method must be overriden");
		}
		#endregion ====================================================================

		#region ======================== Helpers ======================================
		protected void SetDefaultStatus() { Status = "Waiting for files"; }
		protected void SetTotalErrors(int numberOfRetries) {
			if (numberOfRetries == (NumberOfRetries - 1)) {
				TotalErrors++;
				BarErrorNumber = TotalErrors;
			}
		}
		protected void SetTotalFiles(int numberOfRetries) {
			if (numberOfRetries == NumberOfRetries) {
				TotalFiles++;
				BarTotal = TotalFiles;
			}
		}
		public delegate void SetInfoListBoxHandler(string textToDisplay);
		protected void SetInfoListBox(string textToDisplay) {
			lstInfo.Invoke(new SetInfoListBoxHandler(SetInfoListBox_UI), textToDisplay);
		}
		protected void SetInfoListBox_UI(string textToDisplay) {
			lstInfo.Items.Add(DateTime.Now.ToString("t") + " <> " + textToDisplay + (Common.DebugMode ? "(DEBUG)" : ""));
			lstInfo.TopIndex = lstInfo.Items.Count - 1;
		}
		public delegate void RemoveInfoListItemHandler(string textToFind);
		//[MethodImplAttribute(MethodImplOptions.Synchronized)]
		protected void RemoveInfoListItem(string textToFind) {
			lstInfo.Invoke(new RemoveInfoListItemHandler(RemoveInfoListItem_UI), textToFind);
		}
		protected void RemoveInfoListItem_UI(string textToFind) {
			for (int i = lstInfo.Items.Count - 1; i >= 0; i--)
				if (Regex.IsMatch(lstInfo.Items[i] + "", Regex.Escape(textToFind), RegexOptions.IgnoreCase)) {
					lstInfo.Items.RemoveAt(i);
					Application.DoEvents();
					return;
				}
		}
		protected void ProcessingErrorHandler(Exception ex, ProcessingFile file) {
			SetTotalErrors(file.RetryCount);
			ErrorText = file.Name + ": " +
				ex.Message +
				Environment.NewLine + "Source:" + ex.Source +
				Environment.NewLine + "Target Site:" + ex.TargetSite +
				Environment.NewLine + "Stack Trace:" + ex.StackTrace;
		}
		protected void ProcessingCompleteHandler(ProcessingFile file, DateTime whenProcessStarted, string status) {
			//RemoveInfoListItem(file.Name);
			if (file.RetryCount == (NumberOfRetries - 1) || file.RetryCount == 0) {
				TimeSpan ts = DateTime.Now.Subtract(whenProcessStarted);
				SetInfoListBox(file.Name + ": (" + ts.Minutes.ToString("00") + ":" + ts.Seconds.ToString("00") + ") - " + status);
			}
			SetDefaultStatus();
		}
		private void tmrThreadTimer_Tick(object sender, ElapsedEventArgs e) {
			threadTimer.Enabled = false;
			if (IsThreadAlive)
				processThread.Abort();
		}
		private void InitTimer() {
			threadTimer = new System.Timers.Timer(ThreadTimeOut * 1000 * 60);
			threadTimer.AutoReset = false;
			threadTimer.Elapsed += new System.Timers.ElapsedEventHandler(tmrThreadTimer_Tick);
		}

		#endregion ====================================================================

	}
	public class ProcessingFile {
		public NameValueCollection ErrorMessages = new NameValueCollection();
		public int RetryCount;
		public bool Done { get { return RetryCount == 0; } set { RetryCount = 0; } }
		public string FullName;
		public string Directory { get { return Path.GetDirectoryName(FullName); } }
		public string Name { get { return Path.GetFileName(FullName); } }
		public string NameNoExt { get { return Path.GetFileNameWithoutExtension(FullName); } }
		private string[] filters = null;

		public ProcessingFile(string fileName, int numberOfRetries) : this(fileName, numberOfRetries, new string[0]) { }
		public ProcessingFile(string fileName, int numberOfRetries, string filter) : this(fileName, numberOfRetries, new string[] { filter }) { }
		public ProcessingFile(string fileName, int numberOfRetries, string[] filters) {
			FullName = fileName;
			this.filters = filters;
			RetryCount = numberOfRetries;
		}
		public bool CheckFilter() {
			foreach (string filter in filters)
				if (Regex.IsMatch(Name, filter, RegexOptions.IgnoreCase)) return true;
			return filters == null || filters.Length == 0;
		}
		public int CheckRetryCount(Exception ex) {
			ErrorMessages.Add(ex.Message + "\n" + ex.StackTrace, " ");
			//if(--RetryCount == 0) ExceptionManager.Publish(ex,ErrorMessages);
			return RetryCount;
		}
		public void Delete() { File.Delete(FullName); }
	}
	public class Common {
		public static bool DebugMode { get { string text = ConfigurationManager.AppSettings["DebugMode"]; return text != null && text != ""; } }
		//public static string DirIn { get { return GetPropertyFromConfig("DirIn"); } }
		public static string DirOut { get { return GetPropertyFromConfig("DirOut"); } }
		//public static bool ExtraBCR { get { return Convert.ToBoolean(GetPropertyFromConfig("ExtraBCR")); } }
		public class WebService {
			public static int TimeOut { get { return int.Parse(GetPropertyFromConfig("WebService.Timeout")); } }
		}

		public static string GetPropertyFromConfig(string configTagName) {
			string text = ConfigurationManager.AppSettings[configTagName];
			if (text == null || text.Trim() == "") throw new System.Exception("No config path " + configTagName);
			return text;
		}
	}
}